﻿using BoardGame;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;


namespace GameModelView
{
    /// <summary>Represent one player's move.</summary>
    /// <remarks>Two plies make up a turn.</remarks>
    public class Ply
    {
        public Coordinate From { get; internal set; }
        public Coordinate To { get; internal set; }
        public Piece Captured { get; internal set; }
        public GameStatus PlyStatus { get; internal set; }
        public Piece Promotion { get; internal set; }
        public Piece Moving { get; internal set; }


        internal Ply (Piece piece, int fromX, int fromY, int toX, int toY, Piece newCaptured)
        {
            From = new Coordinate (fromX, fromY);
            To = new Coordinate (toX, toY);
            Moving = piece;
            Captured = newCaptured;
            Promotion = null;
            PlyStatus = GameStatus.Temp;
        }


        public bool IsCaptureEnPassant
        { get { return Moving is Pawn && Captured is Pawn && Captured.At.Y == From.Y; } }

        public bool IsPawnPromotion
        { get { return Promotion != null; } }

        public bool IsQueensideCastle
        { get { return Moving is King && From.X - To.X > 1; } }

        public bool IsKingsideCastle
        { get { return Moving is King && From.X - To.X < -1; } }


        public override string ToString ()  //TODO reduce to proper PGN
        {
            string result = "";

            if (Moving is Pawn)
            {
                if (From.X != To.X)
                    result += From.ToString ();
                result += To.ToString ();
            }
            else if (Moving is King)
            {
                if (IsQueensideCastle)
                    result += "O-O-O";
                else if (IsKingsideCastle)
                    result += "O-O";
                else
                    result += Moving.Letter + To.ToString ();
            }
            else
                result += Moving.Letter + From.ToString () + To.ToString ();

            if (Captured != null)
                result += "x" + Captured.Letter;

            if (Promotion != null)
                result += "=" + Promotion.Letter;

            if (PlyStatus == GameStatus.Checkmate)
                result += "#";
            else if (PlyStatus == GameStatus.Check)
                result += "+";

            return result;
        }
    }


    public class PlyStack
    {
        private ChessModelView game;
        internal List<Ply> items;

        internal PlyStack (ChessModelView chessGame)
        {
            this.game = chessGame;
            Clear ();
        }

        public Ply this[int index] { get { return items[index]; } }
        public Ply Top { get { return items[items.Count - 1]; } }
        public int Count { get { return items.Count; } }


        internal void Clear ()
        {
            if (items == null)
                items = new List<Ply> ();
            else
                items.Clear ();
        }


        internal void Push (Piece piece, int toX, int toY)
        {
            Piece captured = game.Board.squares[toX, toY] as Piece;

            // Capture en passant.
            if (piece is Pawn)
                if (piece.At.X != toX && captured == null)
                {
                    captured = (Piece) game.Board.squares[toX, piece.At.Y];
                    game.Board.squares[toX, piece.At.Y] = Square.Empty;
                }

            Ply ply = new Ply (piece, piece.At.X, piece.At.Y, toX, toY, captured);
            items.Add (ply);

            if (piece is King)
            {
                if (ply.IsQueensideCastle)
                {
                    Rook rook = (Rook) game.Board.squares[0, ply.From.Y];
                    game.Board.squares[ply.To.X + 1, ply.To.Y] = rook;
                    game.Board.squares[0, ply.To.Y] = Piece.Empty;
                    rook.At = new Coordinate (ply.To.X + 1, ply.To.Y);
                }
                else if (ply.IsKingsideCastle)
                {
                    Rook rook = (Rook) game.Board.squares[game.Board.XSize - 1, ply.To.Y];
                    game.Board.squares[game.Board.XSize - 1, ply.From.Y] = Piece.Empty;
                    game.Board.squares[ply.To.X - 1, ply.To.Y] = rook;
                    rook.At = new Coordinate (ply.To.X - 1, ply.To.Y);
                }
            }

            if (piece.NeverMoved)
                piece.firstMoved = items.Count - 1;

            if (captured != null)
                captured.IsCaptured = true;

            game.Board.squares[piece.At.X, piece.At.Y] = Square.Empty;
            piece.At = new Coordinate (toX, toY);

            // Handle pawn promotion.
            if (piece is Pawn)
            {
                int lastRank = piece.Side == Player.White? game.Board.YSize - 1 : 0;
                if (piece.At.Y == lastRank)
                {
                    Type promo = game.Promotions[(int) piece.Side];
                    object[] args = new object[] { piece.Side };
                    Piece newPiece = (Piece) Activator.CreateInstance (promo, args);
                    newPiece.Game = game;
                    newPiece.At = new Coordinate (toX, toY);
                    game.Pieces.Replace (piece, newPiece);
                    ply.Promotion = newPiece;
                    ply.Moving = (Pawn) piece;
                    piece = newPiece;
                }
            }

            game.Board.squares[toX, toY] = piece;
        }


        internal void Pop ()
        {
            Ply ply = items[items.Count - 1];
            Piece piece = game.Board.squares[ply.To.X, ply.To.Y] as Piece;

            if (ply.Promotion != null)
            {
                Debug.Assert (piece == ply.Promotion);
                Piece oldPawn = ply.Moving;
                game.Pieces.Replace (piece, oldPawn);
                piece = oldPawn;
            }

            if (piece is King)
            {
                if (ply.IsQueensideCastle)
                {
                    Rook rook = (Rook) game.Board.squares[ply.To.X + 1, ply.To.Y];
                    game.Board.squares[ply.To.X + 1, ply.To.Y] = Piece.Empty;
                    game.Board.squares[0, ply.To.Y] = rook;
                    rook.At = new Coordinate (0, ply.To.Y);
                }
                else if (ply.IsKingsideCastle)
                {
                    Rook rook = (Rook) game.Board.squares[ply.To.X - 1, ply.To.Y];
                    game.Board.squares[ply.To.X - 1, ply.To.Y] = Piece.Empty;
                    game.Board.squares[game.Board.XSize - 1, ply.To.Y] = rook;
                    rook.At = new Coordinate (game.Board.XSize - 1, ply.To.Y);
                }
            }

            game.Board.squares[ply.From.X, ply.From.Y] = piece;
            piece.At = new Coordinate (ply.From.X, ply.From.Y);
            game.Board.squares[ply.To.X, ply.To.Y] = Square.Empty;

            if (ply.Captured != null)
            {
                if (ply.IsCaptureEnPassant)
                {
                    ply.Captured.At = new Coordinate (ply.To.X, piece.At.Y);
                    game.Board.squares[ply.To.X, piece.At.Y] = ply.Captured;
                }
                else
                {
                    ply.Captured.At = new Coordinate (ply.To.X, ply.To.Y);
                    game.Board.squares[ply.To.X, ply.To.Y] = ply.Captured;
                }

                ply.Captured.IsCaptured = false;
            }

            items.RemoveAt (items.Count - 1);

            if (piece.firstMoved == items.Count)
                piece.firstMoved = -1;
        }


        public override string ToString ()
        {
            string result = "";
            foreach (Ply ply in items)
            {
                if (result.Length > 0)
                    result += "; ";

                result += ply.ToString ();
            }

            if (items.Count > 0)
            {
                if ((game.Status & GameStatus.Tie) != 0)
                    result += " 1/2-1/2";
            }

            return result;
        }
    }
}
